package cate.game.pvp.minecraft.data;

import cate.common.table.pvp.minecraft.MinecraftExcel;
import cate.common.table.pvp.minecraft.mine_area_base.MinecraftMineAreaBaseRow;
import cate.common.table.pvp.minecraft.mine_area_rob_item.MinecraftMineAreaRobItemRow;
import cate.common.table.res.drop.DropExcel;
import cate.common.util.TimeTool;
import cate.common.util.random.RandomDropItem;
import cate.game.play.part.PlaySide;
import cate.game.pvp.minecraft.vo.MinecraftCollectionPointVO;
import cate.game.res.MixRes;
import cate.game.res.MixResItem;
import cate.game.res.drop.DropConfigHelper;
import cate.game.role.RoleSnapshot;
import cate.game.role.farm.island.po.HeroState;
import cate.game.util.GameKit;
import com.fasterxml.jackson.annotation.JsonIgnore;
import easy.java.dev.note.NoteClass;
import easy.java.dev.note.NoteField;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.annotation.Transient;

import java.util.*;

@Slf4j
@NoteClass("矿点数据")
@Data
@NoArgsConstructor
public class MinecraftCollectionPointData {
    @NoteField("矿点配置ID")
    private int tid;
    @NoteField("矿点所在层")
    private int layer;

    @NoteField("防守玩家")
    private RoleSnapshot guardPlayer;
    @NoteField("防守方阵容")
    private List<PlaySide> guards;
    @NoteField("血量继承")
    private List<HeroState> heroStates;

    @NoteField("可掠夺奖励")
    public MixRes dangerRewards;

    @NoteField("占领时间")
    private long holdTime;
    @NoteField("上次挂机领取时间")
    private long lastHangGetTime;
    @NoteField("上次挂机结算时间")
    private long lastHangCalcTime;
//    @NoteField("保护时间")
//    private long protectTime;
    @NoteField("占领锁定结束时间")
    private long lockedTime;
    @NoteField("掉落额外的分钟数")
    private Map<Integer, Integer> dropExtraMinutes = new HashMap<>();

    @NoteField("挂机定时器Token")
    public String hangToken;

    @Transient
    @NoteField("是否战斗中")
    public transient boolean fighting;
    @Transient
    @NoteField("战斗方")
    public transient RoleSnapshot fighter;
    @Transient
    @NoteField("战斗定时器Token")
    public transient String fightToken;

    @Transient
    @NoteField("前端结构")
    private transient MinecraftCollectionPointVO vo;

    public MinecraftCollectionPointData(int layer, int tid) {
        this.tid = tid;
        this.layer = layer;
        guards = new ArrayList<>();
        heroStates = new ArrayList<>();
        dangerRewards = new MixRes();
    }

    public void init() {
        if (guards == null) {
            guards = new ArrayList<>();
        }
        if (heroStates == null) {
            heroStates = new ArrayList<>();
        }
        if (dangerRewards == null) {
            dangerRewards = new MixRes();
        }
    }

    /**
     * 数据清理
     */
    public void clear() {
        guardPlayer = null;
        guards.clear();
        heroStates.clear();

        dangerRewards.clear();

        holdTime = 0;
        lastHangGetTime = 0;
        lastHangCalcTime = 0;
//        protectTime = 0;
        lockedTime = 0;
        dropExtraMinutes.clear();

        hangToken = null;

//        fighting = false;
//        fighter = null;
//        fightToken = null;
    }

    /**
     * 转客户端数据结构
     */
    public MinecraftCollectionPointVO toVO(boolean detail, long protectTime) {
        if (vo == null) {
            vo = new MinecraftCollectionPointVO();
        }
        vo.detail = detail;
        vo.tid = tid;
        vo.layer = layer;
        vo.guardPlayer = guardPlayer;
        if (guards != null) {
            vo.totalPower = 0;
            guards.forEach(ps -> vo.totalPower += ps.getTotalPower());
        }

        if (detail) {
            vo.guards = guards;
            vo.heroStates = heroStates;
        } else {
            vo.guards = null;
            vo.heroStates = null;
        }
        vo.dangerRewards = dangerRewards;
        vo.holdTime = holdTime;
        vo.lastHangGetTime = lastHangGetTime;
        vo.protectTime = protectTime;
        vo.lockedTime = lockedTime;
        vo.fighting = fighting;
        return vo;
    }

    /**
     * 开始战斗
     *
     * @param fighter 战斗发起方
     * @return 战斗Token
     */
    public String beginFight(RoleSnapshot fighter) {
        setFighting(true);
        setFighter(fighter);
        setFightToken(GameKit.uid());
        return getFightToken();
    }

    /**
     * 强制结束战斗
     */
    public String cancelFighting() {
        setFighting(false);
        setFighter(null);
        String token = getFightToken();
        setFightToken(null);
        return token;
    }

    @JsonIgnore
    public MinecraftMineAreaBaseRow getConfig(MinecraftExcel minecraft) {
        return minecraft.mine_area_base.get(tid);
    }

    /**
     * 计算挂机奖励
     */
    public long calcHangReward(MinecraftExcel minecraft, DropExcel dropExcel) {
        // 没有占领的点位不结算挂机奖励
        if (getGuardPlayer() == null) {
            return 0L;
        }
        MinecraftMineAreaBaseRow config = getConfig(minecraft);
        if (config == null) {
            return 0L;
        }
        log.info(this + " 计算了挂机奖励");
        long nowTime = System.currentTimeMillis();
        long get_period_min = (nowTime - lastHangGetTime) / TimeTool.TimeCons.MINUTE;
        long calc_period_min = (lastHangCalcTime - lastHangGetTime) / TimeTool.TimeCons.MINUTE;
        if (calc_period_min >= config.hangMaxMinutes) {
            log.info(this + " 停止计算挂机奖励");
            return 0L;
        }
        int actual_min = (int) (Math.min(config.hangMaxMinutes, get_period_min) - calc_period_min);
        // 计算产出
        if (actual_min > 0) {
            lastHangCalcTime += actual_min * TimeTool.TimeCons.MINUTE;
            calcHangRewardByMinutes(minecraft, dropExcel, actual_min);
        }
        if (actual_min + calc_period_min >= config.hangMaxMinutes) {
            log.info(this + " 停止计算挂机奖励");
            return 0L;
        } else {
            return Math.max(1, (lastHangCalcTime + TimeTool.TimeCons.MINUTE - nowTime) / TimeTool.TimeCons.SECOND + 1);
        }
    }

    /**
     * 计算X分钟收益
     *
     * @param minecraft 配置
     * @param minutes   分钟数
     */
    public void calcHangRewardByMinutes(MinecraftExcel minecraft, DropExcel dropExcel, int minutes) {
        MinecraftMineAreaBaseRow config = getConfig(minecraft);
        if (config == null) {
            return;
        }
        // 基础产出
        MixRes baseRes = new MixRes(config.hangResPerMinuteStr).multiply(minutes);
        addToResList(minecraft, dangerRewards, baseRes);
        // 额外产出
        for (RandomDropItem item : config.randomDropList) {
            MixRes addedRes = calcSingleDropId(dropExcel, item, minutes);
            if (!addedRes.isEmpty()) {
                addToResList(minecraft, dangerRewards, addedRes);
            }
        }
    }

    /**
     * 按照配置叠加到奖励中
     *
     * @param minecraft 配置
     * @param targetRes 目标奖励
     * @param addedRes  新增奖励
     */
    private void addToResList(MinecraftExcel minecraft, MixRes targetRes, MixRes addedRes) {
        for (MixResItem item : addedRes.items) {
            MinecraftMineAreaRobItemRow config = minecraft.mine_area_rob_item.findItem(item.type, item.tid);
            if (config == null || config.overlay == 1) {
                targetRes.addItem(item);
            } else {
                if (item.num >= 100) {
                    log.error("不可叠加物品掉落过多! " + item);
                }
                for (int i = 0; i < item.num; i++) {
                    targetRes.items.add(new MixResItem(item.type, item.tid, 1));
                }
            }
        }
    }

    /**
     * 计算单个掉落ID的掉落物品
     *
     * @param dropExcel 掉落配置
     * @param item      随机物品
     * @param minute    计算时间
     */
    private MixRes calcSingleDropId(DropExcel dropExcel, RandomDropItem item, int minute) {
        MixRes mixRes = new MixRes();
        int real_min = minute + dropExtraMinutes.getOrDefault(item.dropId, 0);
        int calc_num = real_min / item.interval;
        int extra_min = real_min % item.interval;
        for (int i = 0; i < calc_num; i++) {
            if (new Random().nextInt(100) <= item.weight) {
                mixRes.addList(DropConfigHelper.getDropReward(dropExcel.config.get(item.dropId)));
            }
        }
        if (extra_min > 0) {
            dropExtraMinutes.put(item.dropId, extra_min);
        } else {
            dropExtraMinutes.remove(item.dropId);
        }
        return mixRes;
    }

    /**
     * 计算打劫后的双方奖励
     *
     * @param minecraft  配置
     * @param oldMailRes 败者奖励
     * @param robGotRes  胜者奖励
     * @param lostRes    损失奖励
     * @param remainRes  剩余奖励
     */
    public void calcRob(MinecraftExcel minecraft,
                        MixRes oldMailRes,
                        MixRes robGotRes,
                        MixRes lostRes,
                        MixRes remainRes) {
        // 可掠夺奖励根据掠夺配置计算
        for (MixResItem item : dangerRewards.items) {
            MinecraftMineAreaRobItemRow config = minecraft.mine_area_rob_item.findItem(item.type, item.tid);
            if (config == null) {
                oldMailRes.addItem(item.copy());
            } else {
                long wasteNum = item.num * config.wasteRate / 10000;
                boolean robbed = new Random().nextInt(10000) < config.robRate;
                if (robbed) {
                    long robbedNum = item.num * config.robNumRate / 10000;
                    if (robbedNum > 0) {
                        robGotRes.addItem(new MixResItem(item.type, item.tid, robbedNum));
                        lostRes.addItem(new MixResItem(item.type, item.tid, robbedNum));
                    }
                    if (item.num - wasteNum - robbedNum > 0) {
                        oldMailRes.addItem(new MixResItem(item.type, item.tid, item.num - wasteNum - robbedNum));
                        remainRes.addItem(new MixResItem(item.type, item.tid, item.num - wasteNum - robbedNum));
                    }
                } else {
                    if (item.num - wasteNum > 0) {
                        oldMailRes.addItem(new MixResItem(item.type, item.tid, item.num - wasteNum));
                        remainRes.addItem(new MixResItem(item.type, item.tid, item.num - wasteNum));
                    }
                }
            }
        }
        dangerRewards.clear();
    }

    /**
     * 领取挂机奖励
     *
     * @return 挂机奖励
     */
    public MixRes getHangReward() {
        MixRes reward = new MixRes();
        reward.addList(dangerRewards.copy());
        dangerRewards.clear();
        lastHangGetTime = (System.currentTimeMillis() / 1000) * 1000;
        lastHangCalcTime = lastHangGetTime;
        return reward;
    }

    /**
     * 强制结算奖励
     *
     * @return 结算奖励
     */
    public MixRes forceSettle() {
        MixRes reward = new MixRes();
        reward.addList(dangerRewards.copy());
        dangerRewards.clear();
        return reward;
    }

    @Override
    public String toString() {
        return "矿点-" + layer + "层" + tid + "号点位";
    }
}
